/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author        Notes
 */

#include <rtthread.h>

#include "drv_gpio.h"
#include "drv_key.h"

#define DBG_TAG "drv.key"
#include <drv_log.h>

static struct drv_key_device _key;
static struct key_state _key_state[KEY_ID_MAX] = {0};
static rt_timer_t k_timer = RT_NULL;

static uint8_t get_key_pin_state(uint8_t key_id)
{
    if (key_id == KEY_ID_UP)
    {
        return rt_pin_read(GET_PIN(A, 0)) == PIN_HIGH ? 1 : 0;
    }
    else if (key_id == KEY_ID_DOWN)
    {
        return rt_pin_read(GET_PIN(E, 3)) == PIN_LOW ? 1 : 0;
    }
    else if (key_id == KEY_ID_LEFT)
    {
        return rt_pin_read(GET_PIN(E, 2)) == PIN_LOW ? 1 : 0;
    }
    else if (key_id == KEY_ID_RIGHT)
    {
        return rt_pin_read(GET_PIN(E, 4)) == PIN_LOW ? 1 : 0;
    }

    return 0;
}

static void key_scan(void)
{
    static uint8_t count = 0;
    uint8_t i = 0;

    // 单次检测
    for (i = 0; i < KEY_ID_MAX; i++)
    {
        if (get_key_pin_state(i) == 1)
        {
            if (_key_state[i].inspect.press_flag == KEY_SCAN_NULL)
            {
                _key_state[i].inspect.press_flag = KEY_SCAN_DEBOUNCE;
            }
            else if (_key_state[i].inspect.press_flag == KEY_SCAN_DEBOUNCE)
            {
                _key_state[i].inspect.press_flag = KEY_SCAN_PRESS;
                _key_state[i].message.state = KEY_SCAN_PRESS;

                if (_key.press_handle != RT_NULL)
                {
                    _key.press_handle(i);
                }
            }
        }
        else 
        {
            if (_key_state[i].inspect.press_flag == KEY_SCAN_PRESS)
            {
                if (_key.realese_handle != RT_NULL)
                {
                    _key.realese_handle(i);
                }
            }

            _key_state[i].inspect.press_flag = KEY_SCAN_NULL;
            _key_state[i].message.state = KEY_SCAN_NULL;
        }
    }

    count++;
    if (count >= (KEY_SCAN_TIME / DEBOUNCE_TIME))
    {
        count = 0;
        
        for (i = 0; i < KEY_ID_MAX; i++)
        {
            _key_state[i].inspect.prev_state = _key_state[i].inspect.cur_state;

            _key_state[i].inspect.cur_state = _key_state[i].inspect.press_flag == KEY_SCAN_PRESS ? KEY_SCAN_PRESS : KEY_SCAN_NULL;

            // 单次按下
            if (_key_state[i].inspect.cur_state == KEY_SCAN_PRESS)
            {
                _key_state[i].inspect.null_times = 0;
                _key_state[i].inspect.press_state = 1;

                // 长按检测
                if (_key_state[i].inspect.prev_state == KEY_SCAN_PRESS)
                {
                    _key_state[i].inspect.long_flag = 1;
                }
                else 
                {
                    _key_state[i].inspect.long_flag = 0;
                    _key_state[i].inspect.long_times = 0;
                }

                // 长按检测中
                if (_key_state[i].inspect.long_flag == 1)
                {
                    _key_state[i].inspect.long_times++;

                    if (_key_state[i].inspect.long_times > (LONG_PRESS_TH / KEY_SCAN_TIME)) // 超过0.8s视为长按
                    {
                        _key_state[i].inspect.press_times = -1;
                    }
                }
                else
                {
                    if (_key_state[i].inspect.press_times == -1) // 旧的长按被处理成点按
                    {
                        _key_state[i].inspect.press_times = 1;
                    }

                    _key_state[i].inspect.press_times++;
                }
            }
            else 
            {
                if (_key_state[i].inspect.press_state == KEY_SCAN_NULL)
                {
                    // 从来没按下
                    continue;
                }
                else  
                {
                    _key_state[i].inspect.null_times++;
                }
            }

            // 检测出口
            if (_key_state[i].inspect.press_state == 1 && _key_state[i].inspect.null_times > (STOP_PRESS_TH / KEY_SCAN_TIME))
            {
                // 打包按键消息
                _key_state[i].message.key_id = i;
                if (_key_state[i].inspect.press_times == -1)
                {
                    _key_state[i].message.press_type = KEY_PRESS_LONG;
                    _key_state[i].message.long_time = _key_state[i].inspect.long_times;
                    _key_state[i].message.series_times = 0;
                }
                else 
                {
                    _key_state[i].message.press_type = KEY_PRESS_SERIES;
                    _key_state[i].message.long_time = 0;
                    _key_state[i].message.series_times = _key_state[i].inspect.press_times;
                }
                if (_key.msg_handle != RT_NULL)
                {
                    _key.msg_handle(_key_state[i].message); // 执行按键消息回调函数
                }

                _key_state[i].inspect.null_times = 0;
                _key_state[i].inspect.press_state = 0;
                _key_state[i].inspect.long_times = 0;
                _key_state[i].inspect.long_flag = 0;
                _key_state[i].inspect.prev_state = 0;
                _key_state[i].inspect.cur_state = 0;
                _key_state[i].inspect.press_times = 0;

                continue;
            }
        }
    }
}

void key_pin_init(void)
{
    rt_pin_mode(GET_PIN(A, 0), PIN_MODE_INPUT_PULLDOWN);
    rt_pin_mode(GET_PIN(E, 2), PIN_MODE_INPUT_PULLUP);
    rt_pin_mode(GET_PIN(E, 3), PIN_MODE_INPUT_PULLUP);
    rt_pin_mode(GET_PIN(E, 4), PIN_MODE_INPUT_PULLUP);
}

//////////////////////////////////////////////////////////////////////////////////////////////////
#if KEY_SCAN_ENTITY == KEY_SCAN_BY_THREAD
static void key_thread_entry(void *para)
{
    while (1)
    {
        key_scan();

        rt_thread_mdelay(DEBOUNCE_TIME);
    }
}
#elif KEY_SCAN_ENTITY == KEY_SCAN_BY_TIMER
static void scan_timeout(void *parameter)
{
    key_scan();
}
#endif

static rt_err_t drv_key_init(rt_device_t dev)
{
    key_pin_init();

    return RT_EOK;
}

static rt_err_t drv_key_open(rt_device_t dev, rt_uint16_t oflag)
{
#if KEY_SCAN_ENTITY == KEY_SCAN_BY_THREAD
    rt_thread_t tid = RT_NULL;

    tid = rt_thread_create("key", key_thread_entry, RT_NULL, 2048, 23, 10);
    if (tid ==RT_NULL)
    {
        return RT_ERROR;
    }
    rt_thread_startup(tid);

    return RT_EOK;

#elif KEY_SCAN_ENTITY == KEY_SCAN_BY_TIMER
    k_timer = rt_timer_create("key", scan_timeout, RT_NULL, DEBOUNCE_TIME, RT_TIMER_FLAG_PERIODIC);
    if (k_timer == RT_NULL)
    {
        return RT_ERROR;
    }
    rt_timer_start(k_timer);

    return RT_EOK;
#endif 
}

static rt_err_t drv_key_close(rt_device_t dev)
{
#if KEY_SCAN_ENTITY == KEY_SCAN_BY_THREAD
    rt_thread_t tid = RT_NULL;

    tid = rt_thread_find("key");
    if (tid ==RT_NULL)
    {
        return RT_ERROR;
    }
    rt_thread_delete(tid);

    return RT_EOK;

#elif KEY_SCAN_ENTITY == KEY_SCAN_BY_TIMER
    if (k_timer == RT_NULL)
    {
        return RT_ERROR;
    }
    rt_timer_delete(k_timer);

    return RT_EOK;
#endif 
}

static rt_size_t drv_key_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    RT_ASSERT(dev);
    RT_ASSERT(size <= sizeof(struct key_press_message));

    if (pos >= KEY_ID_MAX)
    {
        return 0;
    }

    *(struct key_press_message *)buffer = _key_state[pos].message;
    rt_memset(&_key_state[pos].message, 0, sizeof(struct key_press_message));

    return sizeof(struct key_press_message);
}

static rt_err_t drv_key_control(rt_device_t dev, int cmd, void *args)
{
    struct drv_key_device *key = (struct drv_key_device *)dev;
    struct key_press_message *message = (struct key_press_message*)args;

    switch (cmd)
    {
    case KEY_CTRL_SET_PRESS_HANDLE:
    {
        key->press_handle = (void (*)(uint8_t))args;
    }
    break;
    case KEY_CTRL_SET_RELEASE_HANDLE:
    {
        key->realese_handle = (void (*)(uint8_t))args;
    }
    break;
    case KEY_CTRL_SET_MSG_HANDLE:
    {
        key->msg_handle = (void (*)(struct key_press_message))args;
    }
    break;
    case KEY_CTRL_GET_KEY_STATE:
    {
        *message = _key_state[message->key_id].message;
    }
    break;
    }

    return RT_EOK;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops key_ops = {
    drv_key_init,
    drv_key_open,
    drv_key_close,
    drv_key_read,
    RT_NULL,
    drv_key_control
};
#endif

int drv_key_hw_init(void)
{
    rt_err_t result = RT_EOK;
    struct rt_device *device = &_key.parent;

    /* memset _key to zero */
    rt_memset(&_key, 0x00, sizeof(_key));

    _key.press_handle = RT_NULL;
    _key.realese_handle = RT_NULL;
    _key.msg_handle = RT_NULL;

    device->type = RT_Device_Class_Miscellaneous;
#ifdef RT_USING_DEVICE_OPS
    device->ops = &key_ops;
#else
    device->init = drv_key_init;
    device->open = drv_key_open;
    device->close = drv_key_close;
    device->read = drv_key_read;
    device->control = drv_key_control;
#endif
    device->user_data = RT_NULL;
    /* register key device */
    result = rt_device_register(device, "key", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
    if (result != RT_EOK)
    {
        LOG_E("key register err code: %d", result);
        return result;
    }
    LOG_I("key register success");

    return result;
}
INIT_DEVICE_EXPORT(drv_key_hw_init);

//////////////////////////////////////////////////////////////////////////////////////////////////
#if DEMO   
// 参考示例代码
void press_handle(uint8_t key_id)
{
    rt_kprintf("key<%d> pressing!\r\n", key_id);
}

void realese_handle(uint8_t key_id)
{
    rt_kprintf("key<%d> realese!\r\n", key_id);
}

void msg_handle(struct key_press_message message)
{
    rt_kprintf("key<%d> press ", message.key_id);
    if (message.press_type == KEY_PRESS_LONG)
    {
        rt_kprintf("long %d ms!\r\n", (message.long_time * KEY_SCAN_TIME));
    }
    else if (message.press_type == KEY_PRESS_SERIES)
    {
        rt_kprintf("series %d!\r\n", message.series_times);
    }
}

int my_test(void)
{
    rt_device_t dev = RT_NULL;
    struct key_press_message message;

    dev = rt_device_find("key"); 
    if (dev == RT_NULL)
    {
        rt_kprintf("key device not found!\r\n");
        return -RT_ENOSYS;
    }
    rt_kprintf("key device found!\r\n");

    dev->init(dev);
    dev->open(dev, RT_DEVICE_FLAG_RDWR);
    dev->control(dev, KEY_CTRL_SET_PRESS_HANDLE, press_handle);
    dev->control(dev, KEY_CTRL_SET_RELEASE_HANDLE, realese_handle);
    dev->control(dev, KEY_CTRL_SET_MSG_HANDLE, msg_handle);

    rt_thread_mdelay(10000);
    if (dev->read(dev, KEY_ID_LEFT, &message, sizeof(message)) > 0)
    {
        rt_kprintf("tt key<%d> press ", message.key_id);
        if (message.press_type == KEY_PRESS_LONG)
        {
            rt_kprintf("long %d.%d s!\r\n", message.long_time / 10, message.long_time % 10);
        }
        else if (message.press_type == KEY_PRESS_SERIES)
        {
            rt_kprintf("series %d!\r\n", message.series_times);
        }
    }
    // dev->close(dev);
}

#endif 
